package cloud.xlh.agent;

import cloud.xlh.infrastructure.utils.StringUtil;
import javassist.*;
import javassist.bytecode.FieldInfo;

import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.net.URLClassLoader;
import java.security.ProtectionDomain;

/**
 * Created with IntelliJ IDEA.
 *
 * @ project name : xlh_osms
 * @ Author: XuLeHuang
 * @ Date: 2023/6/3 21:50
 * @ Description:
 */
public class MyTransformer implements ClassFileTransformer {
    private URLClassLoader classLoader;
    @Override
    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        if (!className.equals("cloud/xlh/domin/po/UserPO")) {
            return null; // 如果返回null则字节码不会被修改
        }
        System.out.println("进来了");
        try {
            //借助JavaAssist工具，进行字节码插桩
            ClassPool pool = ClassPool.getDefault();
            pool.insertClassPath("D:\\development\\environment\\idea\\IdeaProjects\\xlh_osms\\module\\my-system\\target\\my-system-0.0.1-SNAPSHOT_proguard_base.jar");

            pool.importPackage("cloud.xlh.domin.po");
            CtClass ctClassPO = pool.get("cloud.xlh.domin.po.UserPO");
            CtField[] declaredFields = ctClassPO.getDeclaredFields();
            ctClassPO.stopPruning(true);
            CtClass ctClass = pool.makeClass("cloud.xlh.my_system.service.impl.UserMethodAccess");
            ctClass.stopPruning(true);
            ctClassPO.defrost();
            String simpleName = ctClassPO.getSimpleName();
            String operatorObjectName = StringUtility.setFirstByteLowerCase(simpleName);
            //CtMethod setMethod = new CtMethod(CtClass.voidType, "set", new CtClass[]{pool.get(Object.class.getName()), pool.get(String.class.getName()) , pool.get(Object.class.getName())}, ctClass);

            String src = getMethodBody(simpleName, operatorObjectName, declaredFields);
            CtMethod setMethod = CtNewMethod.make(src, ctClass);
            //setMethod.setBody(src.toString());
            ctClass.addMethod(setMethod);
            //ctClass.writeFile(System.getProperty("user.dir") + "\\target\\classes");
            ctClass.defrost();
            Class aClass = ctClass.toClass();

            //ctClass.writeFile(System.getProperty("user.dir") + "\\target\\classes");
            //ClassLoader classLoader = ctClassPO.toClass().getClassLoader();
            ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
            DynamicClassLoader dynamicClassLoader = new DynamicClassLoader(contextClassLoader);
            dynamicClassLoader.loadClass("cloud.xlh.my_system.service.impl.UserMethodAccess" , ctClass.toBytecode());
            ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            systemClassLoader.loadClass("cloud.xlh.my_system.service.impl.UserMethodAccess");
            System.out.println(systemClassLoader.toString());
            return null;

        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    private String getMethodBody(String simpleName, String operatorObjectName, CtField[] declaredFields) {
        StringBuilder setMethodToString = new StringBuilder("public void set(Object o, String methodName,Object arg)");
        setMethodToString = setMethodToString.append("{");
        setMethodToString.append("\r\n");
        setMethodToString.append(String.format("%1$s %2$s = (%1$s) o;", simpleName, operatorObjectName));

        for (CtField declaredField : declaredFields) {
            //将首字母大写
            FieldInfo fieldInfo = declaredField.getFieldInfo();
            String descriptor = fieldInfo.getDescriptor();
            String name = fieldInfo.getName();
            String methodName = "set" + StringUtil.firstCharToUpCaseForWord(declaredField.getName());
            setMethodToString.append("\r\n");
            setMethodToString.append(String.format("if (methodName.equalsIgnoreCase(\"%1$s\") || methodName.equalsIgnoreCase(\"%2$s\")) {"
                    , methodName.toLowerCase(), methodName.substring(3)));
            setMethodToString.append("\r\n");
            //todo
            setMethodToString.append(String.format("%1$s.%2$s((%3$s) arg);}", operatorObjectName, methodName, descriptor.substring(1, descriptor.length() - 1)).replace("/", "."));
            setMethodToString.append("\r\n");
        }
        setMethodToString.append("}");
        return setMethodToString.toString();
    }
}
